#ifdef _FNT_VIEWER_OPTIONS_H_

ViewerGeneralOptions* ViewerGeneralOptions::_instance{nullptr};
OptionsPage* OptionsPage::viewerGeneralOptions() {
	return ViewerGeneralOptions::instance();
}

static std::array<int, 11> scale_factors{0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5};

ViewerGeneralOptions::ViewerGeneralOptions():
	OptionsPage{"General", "General rendering settings"},
	vp{nullptr}, disabled{true}
{
	auto options=Tracer::instance()->options();
	if(!options->getInt("viewer.scale.factor", &scaleDef))
		scaleDef=0;

	auto flayout=new QFormLayout{};
	layout()->addLayout(flayout, 0);
	layout()->addStretch(1);

	comboBox=new QComboBox{this};
	flayout->addRow("&Magnification:", comboBox);
	comboBox->setEditable(false);
	int idx=-1;
	for(size_t i=0; i<scale_factors.size(); i++) {
		auto f=scale_factors[i];
		if(f<0) {
			comboBox->addItem(QString{"1/%1"}.arg(1-f), QVariant{f});
		} else {
			comboBox->addItem(QString{"%1"}.arg(1+f), QVariant{f});
		}
		if(scale_factors[i]==scaleDef)
			idx=i;
	}
	if(idx==-1) {
		idx=0;
		scaleDef=scale_factors[0];
	}
	comboBox->setCurrentIndex(idx);
	scaleSet=scaleDef;
	comboBox->setToolTip("Set the magnification factor for 3D visualization.\nLarger value is used to reduce GPU usage.");
	connect(comboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ViewerGeneralOptions::scaleChanged);
}
ViewerGeneralOptions::~ViewerGeneralOptions() {
}

	void ViewerGeneralOptions::getState(bool* a, bool* b, bool* c, bool* d) const {
		*a=*b=(!disabled && vp && vp->scale_factor!=scaleSet);
		*c=*d=scaleDef!=scaleSet;
	}
	void ViewerGeneralOptions::setStateImp(SessionState ts) {
		switch(ts) {
			case SessionState::Invalid:
			case SessionState::LoadingCatalog:
			case SessionState::Readonly:
				disabled=true;
				break;
			case SessionState::Ready:
			case SessionState::LoadingCubes:
			case SessionState::Computing:
				disabled=false;
				break;
		}
	}
	void ViewerGeneralOptions::useDefaultImp() {
		for(size_t i=0; i<scale_factors.size(); i++) {
			if(scale_factors[i]==scaleDef) {
				comboBox->setCurrentIndex(i);
				return;
			}
		}
		comboBox->setCurrentIndex(0);
	}
	void ViewerGeneralOptions::saveDefaultImp() {
		if(scaleSet!=scaleDef) {
			scaleDef=scaleSet;
			if(scaleDef==0) {
				Tracer::instance()->options()->removeKey("viewer.scale.factor");
			} else {
				Tracer::instance()->options()->setInt("viewer.scale.factor", scaleDef);
			}
		}
	}
void ViewerGeneralOptions::applyChangesImp() {
	if(!disabled && vp && vp->scale_factor!=scaleSet) {
		vp->scale_factor=scaleSet;
		auto viewer=vp->viewer;
		auto dpr=viewer->devicePixelRatio();
		auto w=viewer->width();
		auto h=viewer->height();
		int ww=dpr*w;
		int hh=dpr*h;

		viewer->makeCurrent();
		vp->resizeFrameBuffers(ww, hh);
		viewer->doneCurrent();

		viewer->update();
	}
}
void ViewerGeneralOptions::resetChangesImp() {
	if(!disabled && vp) {
		auto scale=vp->scale_factor;
		if(scale!=scaleSet) {
			for(size_t i=0; i<scale_factors.size(); i++) {
				if(scale_factors[i]==scale) {
					comboBox->setCurrentIndex(i);
					return;
				}
			}
			comboBox->setCurrentIndex(0);
		}
	}
}
void ViewerGeneralOptions::scaleChanged(int i) {
		scaleSet=scale_factors[i];
		notifyChange();
}

void ViewerGeneralOptions::config(ViewerPriv* _vp, Options* options) const {
	if(!options->getInt("viewer.scale.factor", &_vp->scale_factor))
		_vp->scale_factor=scaleDef;
}
void ViewerGeneralOptions::saveConfig(ViewerPriv* _vp, Options* options) const {
	options->setInt("viewer.scale.factor", _vp->scale_factor);
}
void ViewerGeneralOptions::attach(ViewerPriv* _vp) {
	vp=_vp;
	if(vp) {
		resetChangesImp();
	} else {
		useDefaultImp();
	}
}


ViewerColorOptions* ViewerColorOptions::_instance{nullptr};
OptionsPage* OptionsPage::viewerColorOptions() {
	return ViewerColorOptions::instance();
}

void ViewerColorOptions::config(ViewerPriv* _vp, Options* opt) const {
	int v;
	if(opt->getInt("viewer.color.mode", &v))
		_vp->colorMode=v;
	else
		_vp->colorMode=colorModeDef;

	for(int i=0; i<COLOR_NUM; i++) {
		auto key=QString{"viewer.color.%1"}.arg(i);
		auto vals=opt->getIntList(key);
		if(vals.size()<3) {
			_vp->colors[i]=colorsDef[i];
		} else {
			_vp->colors[i]=QColor{vals[0], vals[1], vals[2]};
		}
	}
}
void ViewerColorOptions::saveConfig(ViewerPriv* _vp, Options* options) const {
	options->setInt("viewer.color.mode", _vp->colorMode);
	for(int i=0; i<COLOR_NUM; i++) {
		auto key=QString{"viewer.color.%1"}.arg(i);
		auto& c=_vp->colors[i];
		options->setIntList(key, {c.red(), c.green(), c.blue()});
	}
}
void ViewerColorOptions::attach(ViewerPriv* _vp) {
	vp=_vp;
	if(vp) {
		m_differentColorForNeuronsCheckMode->setChecked(vp->colorMode);
		for(int i=0; i<COLOR_NUM; i++) {
			colorWidgets[i]->setColor(vp->colors[i]);
		}
	} else {
		m_differentColorForNeuronsCheckMode->setChecked(colorModeDef);
		for(int i=0; i<COLOR_NUM; i++) {
			colorWidgets[i]->setColor(colorsDef[i]);
		}
	}
	notifyChange();
}
ViewerColorOptions::ViewerColorOptions():
	OptionsPage{"Color", "Color Settings"}, vp{nullptr}, disabled{true},
	noupdate{false}
{
	auto options=Tracer::instance()->options();

	int v;
	if(!options->getInt("viewer.color.mode", &v)) {
		v=0;
	}
	colorModeSet=colorModeDef=v;

	m_differentColorForNeuronsCheckMode=new QCheckBox{"Different colors for each neuron", this};
	layout()->addWidget(m_differentColorForNeuronsCheckMode, 0);
	m_differentColorForNeuronsCheckMode->setChecked(colorModeDef);
	m_differentColorForNeuronsCheckMode->setToolTip("If checked, colors are used to denote different neurons.\nOtherwise, colors are used to denote types of branches.");
	connect(m_differentColorForNeuronsCheckMode, &QCheckBox::stateChanged, this, &ViewerColorOptions::checkModeToggled);

	auto flayout=new QFormLayout{};
	layout()->addLayout(flayout, 0);
	for(int i=0; i<COLOR_NUM; i++) {
		auto vals=Tracer::instance()->options()->getIntList(QString{"viewer.color.%1"}.arg(i));
		QColor color{colorData[i].def};
		if(vals.size()>=3) {
			color=QColor{vals[0], vals[1], vals[2]};
		}

		colorsSet[i]=colorsDef[i]=color;
		colorsNotDef[i]=false;

		colorWidgets[i]=new ColorWidget{colorData[i].title, colorData[i].desc, this};
		colorWidgets[i]->setColor(color);
		connect(colorWidgets[i], &ColorWidget::colorChanged, [this, i](const QColor& c) { colorChanged(i, c); });
		flayout->addRow(colorData[i].title, colorWidgets[i]);
	}
	layout()->addStretch(1);
}

ViewerColorOptions::~ViewerColorOptions() {
}
void ViewerColorOptions::getState(bool* a, bool* b, bool* c, bool* d) const {
	*a=false;
	*b=false;
	bool notDefault=colorModeSet!=colorModeDef;
	for(int i=0; i<COLOR_NUM; i++) {
		if(colorsNotDef[i]) {
			notDefault=true;
			break;
		}
	}
	*c=notDefault;
	*d=notDefault;
}
void ViewerColorOptions::setStateImp(SessionState ts) {
	switch(ts) {
		case SessionState::Invalid:
		case SessionState::LoadingCatalog:
		case SessionState::Readonly:
			disabled=true;
			break;
		case SessionState::Ready:
		case SessionState::LoadingCubes:
		case SessionState::Computing:
			disabled=false;
			break;
	}
}
void ViewerColorOptions::useDefaultImp() {
	auto oldv=noupdate;
	noupdate=true;
	if(colorModeDef!=colorModeSet) {
		m_differentColorForNeuronsCheckMode->setCheckState(colorModeDef?Qt::Checked:Qt::Unchecked);
	}
	for(int i=0; i<COLOR_NUM; i++) {
		if(colorsNotDef[i]) {
			colorWidgets[i]->setColor(colorsDef[i]);
		}
	}
	noupdate=oldv;
	if(!noupdate && vp && !disabled)
		vp->viewer->update();
}
void ViewerColorOptions::saveDefaultImp() {
	if(colorModeSet!=colorModeDef) {
		colorModeDef=colorModeSet;
		if(colorModeSet) {
			Tracer::instance()->options()->setInt("viewer.color.mode", colorModeSet);
		} else {
			Tracer::instance()->options()->removeKey("viewer.color.mode");
		}
	}
	for(int i=0; i<COLOR_NUM; i++) {
		if(colorsNotDef[i]) {
			colorsNotDef[i]=false;
			colorsDef[i]=colorsSet[i];
			auto key=QString{"viewer.color.%1"}.arg(i);
			if(colorsSet[i]!=colorData[i].def) {
				Tracer::instance()->options()->setIntList(key, {colorsSet[i].red(), colorsSet[i].green(), colorsSet[i].blue()});
			} else {
				Tracer::instance()->options()->setIntList(key, {});
			}
		}
	}
}
void ViewerColorOptions::checkModeToggled(bool s) {
	colorModeSet=s;
	if(vp && !disabled) {
		vp->colorMode=colorModeSet;
		if(!noupdate)
			vp->viewer->update();
	}
	notifyChange();
}
void ViewerColorOptions::colorChanged(int i, const QColor& col) {
	if(col!=colorsSet[i]) {
		colorsSet[i]=col;
		colorsNotDef[i]=col!=colorsDef[i];
		if(vp && !disabled) {
			vp->colors[i]=col;
			if(!noupdate)
				vp->viewer->update();
		}
		notifyChange();
	}
}

ViewerVolumeOptions* ViewerVolumeOptions::_instance{nullptr};
OptionsPage* OptionsPage::viewerVolumeOptions() {
	return ViewerVolumeOptions::instance();
}

void ViewerVolumeOptions::config(ViewerPriv* _vp, Options* opt) const {
	int v, u;
	if(opt->getInt("viewer.slice.total", &v) && opt->getInt("viewer.slice.shown", &u)) {
		if(v>MAX_SLICES) v=MAX_SLICES;
		if(v<0) v=0;
		if(u>v) u=v;
		if(u<0) u=0;
		_vp->slice_num=v;
		_vp->slice_shown=u;
	} else {
		_vp->slice_num=slice_num_def;
		_vp->slice_shown=slice_shown_def;
	}
}
void ViewerVolumeOptions::saveConfig(ViewerPriv* _vp, Options* opt) const {
	opt->setInt("viewer.slice.total", _vp->slice_num);
	opt->setInt("viewer.slice.shown", _vp->slice_shown);
}
void ViewerVolumeOptions::attach(ViewerPriv* _vp) {
	vp=_vp;
	if(vp) {
		sliderNum->setValue(_vp->slice_num);
		sliderShown->setValue(_vp->slice_shown);
	} else {
		sliderNum->setValue(slice_num_def);
		sliderShown->setValue(slice_shown_def);
	}
	notifyChange();
}
ViewerVolumeOptions::ViewerVolumeOptions():
	OptionsPage{"Volume", "Volume visualization settings"}, vp{nullptr},
	disabled{true}, noupdate{false}
{
	auto options=Tracer::instance()->options();

	int v, u;
	if(!(options->getInt("viewer.slice.total", &v) && options->getInt("viewer.slice.shown", &u))) {
		v=MAX_SLICES;
		u=MAX_SLICES;
	} else {
		if(v>MAX_SLICES) v=MAX_SLICES;
		if(v<0) v=0;
		if(u>v) u=v;
		if(u<0) u=0;
	}
	slice_shown_set=slice_shown_def=u;
	slice_num_set=slice_num_def=v;

	auto flayout=new QFormLayout{};
	layout()->addLayout(flayout, 0);
	layout()->addStretch(1);

	sliderNum=new QSlider{Qt::Horizontal, this};
	flayout->addRow("&Total slices:", sliderNum);
	sliderNum->setMaximum(MAX_SLICES);
	sliderNum->setMinimum(0);
	sliderNum->setValue(slice_num_def);
	sliderNum->setToolTip("Specify total number of slices used for volume rendering.\nLarger value results in better visualization.");
	connect(sliderNum, &QSlider::valueChanged, this, &ViewerVolumeOptions::sliderNumChanged);

	sliderShown=new QSlider{Qt::Horizontal, this};
	flayout->addRow("&Shown slices:", sliderShown);
	sliderShown->setMaximum(MAX_SLICES);
	sliderShown->setMinimum(0);
	sliderShown->setValue(slice_shown_def);
	sliderShown->setToolTip("Specify number of slices actually shown.\nLarger value shows thicker data and uses more GPU.");
	connect(sliderShown, &QSlider::valueChanged, this, &ViewerVolumeOptions::sliderShownChanged);
}
ViewerVolumeOptions::~ViewerVolumeOptions() {
}
void ViewerVolumeOptions::getState(bool* a, bool* b, bool* c, bool* d) const {
	*a=false;
	*b=false;
	bool notDefault=slice_shown_set!=slice_shown_def||slice_num_set!=slice_num_def;
	*c=notDefault;
	*d=notDefault;
}
void ViewerVolumeOptions::setStateImp(SessionState ts) {
	switch(ts) {
		case SessionState::Invalid:
		case SessionState::LoadingCatalog:
		case SessionState::Readonly:
			disabled=true;
			break;
		case SessionState::Ready:
		case SessionState::LoadingCubes:
			disabled=false;
			break;
		case SessionState::Computing:
			disabled=false;
			break;
	}
}
void ViewerVolumeOptions::useDefaultImp() {
	auto oldv=noupdate;
	noupdate=true;

	if(slice_num_set!=slice_num_def) {
		sliderNum->setValue(slice_num_def);
	}
	if(slice_shown_set!=slice_shown_def) {
		sliderShown->setValue(slice_shown_def);
	}

	noupdate=oldv;
	if(!noupdate && vp && !disabled)
		vp->viewer->update();
}
void ViewerVolumeOptions::saveDefaultImp() {
	auto options=Tracer::instance()->options();

	if(slice_num_set!=slice_num_def) {
		slice_num_def=slice_num_set;
		options->setInt("viewer.slice.total", slice_num_def);
	}
	if(slice_shown_set!=slice_shown_def) {
		slice_shown_def=slice_shown_set;
		options->setInt("viewer.slice.shown", slice_shown_def);
	}
}

void ViewerVolumeOptions::sliderNumChanged(int v) {
	slice_num_set=v;
	if(slice_num_set<slice_shown_set)  {
		auto oldv=noupdate; noupdate=true;
		sliderShown->setValue(slice_num_set);
		noupdate=oldv;
	}
	if(vp && !disabled) {
		vp->slice_num=v;
		if(!noupdate)
			vp->viewer->update();
	}
	notifyChange();
}
void ViewerVolumeOptions::sliderShownChanged(int v) {
	slice_shown_set=v;
	if(slice_num_set<slice_shown_set)  {
		auto oldv=noupdate; noupdate=true;
		sliderNum->setValue(slice_shown_set);
		noupdate=oldv;
	}
	if(vp && !disabled) {
		vp->slice_shown=v;
		if(!noupdate)
			vp->viewer->update();
	}
	notifyChange();
}

#endif
